/**************************************************************/
/*                                                            */
/*     MC823 - Laboratorio de Teleprocessamento e Redes       */
/*     1 Semestre / 2013                                      */
/*     Projeto 1 - Cliente e Servidor TCP/UDP de uma Livraria */
/*                                                            */
/*     Alunos: Joao Gabriel M.D.Mendes - RA 102788            */
/*             Lucas Domingues Lima    - RA 103140            */
/*                                                            */
/**************************************************************/

/* Arquivo: /sendrecv.c
   Definicao: funcoes internas ligadas exclusivamente ao 
   			  recebimento e envio de mensagens. Basicamente
   			  recebem string de cliente.c, e chamam rotinas 
   			  do sistema para enviar a mensagem pela rede.
   			  (e o contrario para receber as mensagens).
*/


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>

#define MAXDATASIZE 100
#define RECV_TIMEOUT 5	/* timeout in seconds */
#define MAXDGRAMSIZE 500

//funcao para concatenar duas strings, colocando um '\0' ao final
char* concatena(char *a,char *b)
{
	char *temp;
	int tamA,tamB,i;

	if(a == NULL && b == NULL)
		return NULL;
	if(a == NULL)
		return strdup(b);
	if(b == NULL)
		return strdup(a);

	tamA = strlen(a);
	tamB = strlen(b);

	temp = (char*) malloc(sizeof(char)*(tamA+tamB+1));

	for(i=0;i<tamA;i++)
		temp[i] = a[i];
	for(i=0;i<tamB;i++)
		temp[tamA+i] = b[i];
	temp[tamA+tamB] = '\0';
	
	return temp;
}

//funcao de envio de mensagens usando TCP
int sendall(int s, char *buf, int *len)
{
       int total = 0;            // how many bytes we've sent
       int bytesleft = *len;     // how many we have left to send
       int n;
       while(total < *len) 
       {
           n = send(s, buf+total, bytesleft, 0);
           if (n == -1) { break; }
           total += n;
           bytesleft -= n;
       }
       *len = total;             // return number actually sent here
       return n==-1?-1:0;        // return -1 on failure, 0 on success
}


//funcao de recebimento de mensagens usando TCP
int recvall(int s, char **msg,int *len)
{
       int n,i;
       char buf[MAXDATASIZE],*aux;		
       int total = 0;            // how many bytes we've received

       *msg = NULL;	

       do
       {
           n = recv(s, buf, MAXDATASIZE-1, 0);		
           if (n == -1) { break;}
	       buf[n] = '\0';
           total += n;
	   aux = *msg;	
	   *msg = concatena(*msg,buf);
	   if(aux != NULL)
	       free(aux);
       }while(buf[n-1] != '*');

       (*msg)[total-1] = '\0';   // retira o * da msg, uma vez que o mesmo serve apenas para encapsular 	
       *len = total;             // return number actually received here
       return n==-1?-1:0;        // return -1 on failure, 0 on success
 }

/*funcoes para UDP */ 

/*auxiliar para tratamento de time out*/
static sigjmp_buf recv_timed_out;

/*funcao auxiliar para tratar alarme gerado por time out*/
void trata_alarme(int sig)
{
       signal(SIGALRM, SIG_DFL);
       siglongjmp(recv_timed_out, 1);
}

/*funcao UDP para envio de mensagens*/
int sendallto(int s, char *buf, int *len, const struct sockaddr *to, socklen_t tolen)
{
       int total = 0;            // how many bytes we've sent
       int bytesleft = *len;     // how many we have left to send
       int n,i,j,num,tam;
       char aux[MAXDGRAMSIZE+1];

       num = 0;
       while(total < *len) 
       {
	   aux[0] = 'A'+num;

	   tam = strnlen(&(buf[total]),MAXDGRAMSIZE-1);
           j=1;
           for(i=total;i<total+tam;i++)
           {
               aux[j] = buf[i];
               j++;
           }
           aux[j] = '\0';
           n = sendto(s,aux,strlen(aux),0,to,tolen);
           if (n == -1) { break; }
           total += (n-1);
           bytesleft -= (n-1);
           num++;
       }
       *len = total;             // return number actually sent here
       return n==-1?-1:0;        // return -1 on failure, 0 on success
}

int recvallfrom(int s, char **msg,int *len,struct sockaddr *from, socklen_t *fromlen)
{
       int n,i;
       char buf[MAXDGRAMSIZE+1],*aux,string[26][MAXDGRAMSIZE+1];                
       int total,posi,recebeuTodos,num_pacotes,num_pacotes_lidos;           // how many bytes we've received

       total = 0;
       recebeuTodos = 0;
       num_pacotes = 0;
       num_pacotes_lidos = 0;
       *msg = NULL;
          
       signal(SIGALRM, trata_alarme); // interceptação do sinal ALARME

       do
       {
           if (sigsetjmp(recv_timed_out, 1))
           {
		alarm(0);
                signal(SIGALRM, SIG_DFL);
                return -2;//retorna -2 indicando timeout do recvfrom
               }
           alarm(RECV_TIMEOUT);
           n = recvfrom(s,buf, MAXDGRAMSIZE,0,from,fromlen);            
           if (n == -1) { break;}
           buf[n] = '\0';
           total += (n-1);
           posi = buf[0] - 'A';
           strcpy(string[posi],&(buf[1]));
           if(buf[strlen(buf)-1] == '*')
               num_pacotes = posi+1;
           num_pacotes_lidos++;
       }while(num_pacotes != num_pacotes_lidos);
      
       for(i=0;i<num_pacotes;i++)
       {
           aux = *msg;
           *msg = concatena(aux,string[i]);
           if(aux != NULL)
               free(aux);
       }

       (*msg)[strlen(*msg)-1] = '\0';     // retira o * da msg, uma vez que o mesmo serve apenas para encapsular 
       *len = strlen(*msg);               // return number actually received here
       return n==-1?-1:0;                 // return -1 on failure, 0 on success
 }

